home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 4
/
Meeting Pearls Vol. IV (1996)(GTI - Schatztruhe)[!].iso
/
Pearls
/
util
/
misc
/
CyberCron
/
Source
/
CyberCron.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-10-07
|
63KB
|
2,684 lines
/*
* CyberCron.c
*
* Copyright © 1992 by Christopher A. Wichura (caw@miroc.chi.il.us). All rights
* reserved.
*/
struct RxsLib *RexxSysBase;
unsigned long ARexxLibCount = 0;
/*
* here we have storage for the current crontab file, sendmail command and
* the name of our log file
*/
UBYTE CronTabName[256];
UBYTE SendMailCmd[256];
UBYTE LogFile[256];
/*
* storage for two large buffers. we reuse these in several different
* places within CyberCron
*/
UBYTE BigBufOne[BIG_BUF_SIZE];
UBYTE BigBufTwo[BIG_BUF_SIZE];
/*
* these are used by the ParseEvent() routine when no priority or stack size
* is specified.
*/
ULONG DefaultStackSize = 4096;
BYTE DefaultPriority = 0;
/*
* this global is the list header for all cybernodes. we tell who added the
* event (crontab or via a rexx command) by whether or not the CNB_CRONTAB
* bit is set in the cn_Flags field.
*/
struct List EventList;
#define CYBERCRON GetString(&LocaleInfo, MSG_PROGNAME)
#define ARG_TEMPLATE "CRONTAB/K,LOGFILE/K,SENDMAIL/K,DEFSTACK/K/N,DEFPRI/K/N,TOOLPRI=CRONPRI/K/N,PORTNAME/K"
enum CmdlineReadArgs {
ARG_CRONTAB,
ARG_LOGFILE,
ARG_SENDMAIL,
ARG_STACK,
ARG_PRI,
ARG_CPRI,
ARG_PORTNAME,
ARG_sizeof
};
/* extern references to our version and revision numbers */
extern ULONG __far Version;
extern ULONG __far Revision;
extern UBYTE __far VersionID[];
/* storage for the pointer to StdErr */
BPTR StdErr = NULL;
/* our old task priority */
WORD OldPriority = -1;
/* for our main ReadArgs call so we can free it later */
struct RDArgs *MyArgs = NULL;
struct RDArgs *ArgsPtr = NULL;
STRPTR WBArgs = NULL;
/* stuff used in launching/destroying jobs */
struct MyPublicSema *jobSema;
ULONG NumSystemJobs = 0;
ULONG NumARexxJobs = 0;
/* Semaphore to protect Log() being called under EndSystemJob() */
struct MyPublicSema *logSema;
/* stuff for our timer port */
struct MsgPort *TimerPort = NULL;
struct timerequest TimerIO;
BOOL TimerUP = FALSE;
BOOL DoingTimeRequest = FALSE;
/* stuff for our notify request */
struct NotifyRequest MyNotifyRequest;
BYTE NotifySignal = -1;
BOOL NotifyUP = FALSE;
/* stuff for our rexx port */
struct MsgPort *RexxPort = NULL;
/* global flags */
BOOL BringerDown = FALSE; /* trying to quit ? */
BOOL Suspended = FALSE; /* currently suspended ? */
/* storage for our old pr_WindowPtr */
APTR OldWindowPtr;
/* specifies the maximum number of jobs for each of the queues */
struct JobQueue jobQueue[27];
/*
* flag: should ErrorMsg() try and use requesters instead of writing to
* StdErr?
*/
BOOL ErrorsToStdErr = TRUE;
/* stuff for our locale support */
struct LocaleInfo LocaleInfo;
/* storage for a couple library bases */
struct Library *UtilityBase = NULL;
struct Library *OwnDevUnitBase = NULL;
/* used in some date calculations */
static UBYTE DayTable[2][12] =
{
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
/* this is our main routine */
int __regargs main(char *cmdptr, int cmdlen, struct WBStartup * WBMsg)
{
char *ArgArray[ARG_sizeof];
ULONG NSignal, TSignal, RSignal;
ULONG signals;
ULONG numJobs;
int index;
BPTR lock;
struct timeval tr_time;
#define TextBuf BigBufOne
OldWindowPtr = ((struct Process *) FindTask(NULL))->pr_WindowPtr;
((struct Process *) FindTask(NULL))->pr_WindowPtr = (APTR) - 1;
StdErr = ((struct Process *) FindTask(NULL))->pr_CES;
if (StdErr == NULL)
StdErr = Output();
/*
* the first thing we do is try and open up the local library and
* grab our catalog as we will need it if we want to print any error
* messages or other text
*/
if (LocaleInfo.li_LocaleBase = (APTR) OpenLibrary("locale.library", 38))
LocaleInfo.li_Catalog = OpenCatalogA(NULL, "CyberCron.catalog", NULL);
/*
* if started from the Workbench then we need to make an argstring
* from our tooltypes as well as try and clone Workbench's CLI
* structure
*/
if (WBMsg) {
ErrorsToStdErr = FALSE;
if (!(WBArgs = WBtoCLIargs(WBMsg, ARG_TEMPLATE)))
MyExit(5);
/*
* make us into a CLI so we have a path to propigate to jobs
* we start
*/
WB2CLI(WBMsg, DefaultStackSize, DOSBase);
}
/* try and open OwnDevUnit.library */
OwnDevUnitBase = OpenLibrary(ODU_NAME, 0);
/* try and open up utility.library */
if (!(UtilityBase = OpenLibrary("utility.library", 37))) {
ErrorMsg(MSG_COULDNT_OPEN, "utility.library");
MyExit(20);
}
NewList(&EventList);
if (!(jobSema = InitMyPublicSemaphore(GetString(&LocaleInfo, MSG_JOB_SEMA_NAME), sizeof(UBYTE) * JOB_TABLE_SIZE))) {
ErrorMsg(MSG_NO_PUB_SEMA);
MyExit(5);
}
if (!(logSema = InitMyPublicSemaphore(GetString(&LocaleInfo, MSG_LOG_SEMA_NAME), 0))) {
ErrorMsg(MSG_NO_PUB_SEMA);
MyExit(5);
}
/* do the stuff needed to call ReadArgs to parse the command line */
memset(ArgArray, 0, sizeof(ArgArray));
if (!(MyArgs = (struct RDArgs *) AllocDosObject(DOS_RDARGS, TAG_DONE))) {
ErrorMsg(MSG_NO_RDARGS);
MyExit(5);
}
if (!(MyArgs->RDA_ExtHelp = (UBYTE *) AllocVec(strlen(GetString(&LocaleInfo, MSG_ARG_HELP)) + strlen(GetString(&LocaleInfo, MSG_COPYRIGHT)) + (2 * strlen(CYBERCRON)) + strlen(VersionID) + 10, MEMF_CLEAR))) {
ErrorMsg(MSG_OUTOFMEM);
MyExit(5);
}
sprintf((char *) MyArgs->RDA_ExtHelp, GetString(&LocaleInfo, MSG_ARG_HELP), CYBERCRON, VersionID, GetString(&LocaleInfo, MSG_COPYRIGHT), CYBERCRON);
if (WBArgs) {
MyArgs->RDA_Source.CS_Buffer = WBArgs;
MyArgs->RDA_Source.CS_Length = strlen(WBArgs);
MyArgs->RDA_Source.CS_CurChr = 0L;
}
/* now call ReadArgs to parse the command line */
ArgsPtr = ReadArgs(ARG_TEMPLATE, (LONG *) & ArgArray, MyArgs);
/* free the memory we used for this ReadArgs() call */
FreeVec((char *) MyArgs->RDA_ExtHelp);
FreeVec(WBArgs);
WBArgs = NULL;
if (!ArgsPtr) {
Fault(IoErr(), NULL, TextBuf, BIG_BUF_SIZE_BASE);
ErrorMsg(MSG_STRING_HACK, TextBuf);
MyExit(5);
}
if (ArgArray[ARG_CRONTAB])
if (strlen(ArgArray[ARG_CRONTAB]) + 1 > sizeof(CronTabName)) {
ErrorMsg(MSG_CRONTAB_NAME_TOO_LONG);
MyExit(5);
}
else
strcpy(CronTabName, ArgArray[ARG_CRONTAB]);
else
strcpy(CronTabName, "S:CronTab");
if (ArgArray[ARG_LOGFILE])
if (strlen(ArgArray[ARG_LOGFILE]) + 1 > sizeof(LogFile)) {
ErrorMsg(MSG_LOGFILE_NAME_TOO_LONG);
MyExit(5);
}
else
strcpy(LogFile, ArgArray[ARG_LOGFILE]);
if (ArgArray[ARG_SENDMAIL])
if (strlen(ArgArray[ARG_SENDMAIL]) + 1 > sizeof(SendMailCmd)) {
ErrorMsg(MSG_SENDMAIL_COMMAND_TOO_LONG);
MyExit(5);
}
else
strcpy(SendMailCmd, ArgArray[ARG_SENDMAIL]);
if (ArgArray[ARG_STACK])
DefaultStackSize = *((LONG *) ArgArray[ARG_STACK]);
else {
/*
* if we have a cli attached to us then get the default stack
* size out of it. Otherwise leave it be. WBtoCLIargs() will
* probably have set DefaultStackSize for us already in such
* a case. If not, the hard-coded default of 4096 will be
* used
*/
struct CommandLineInterface *cli;
if (cli = Cli())
DefaultStackSize = sizeof(LONG) * cli->cli_DefaultStack;
}
if (DefaultStackSize < 2048)
DefaultStackSize = 2048;
if (ArgArray[ARG_PRI])
DefaultPriority = *((LONG *) ArgArray[ARG_PRI]) & 0xFF;
if (ArgArray[ARG_CPRI])
OldPriority = SetTaskPri(FindTask(NULL), *((LONG *) ArgArray[ARG_CPRI]) & 0xFF);
/*
* open up our ARexx port. Check to see if we're already using that
* port first, though.
*/
Forbid();
if (FindPort(ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON")) {
/* port already exists so fail */
Permit();
ErrorMsg(MSG_ALREADY_RUNNING, (ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"));
MyExit(5);
}
else {
if (!(RexxPort = CreatePort((ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"), 0))) {
Permit();
ErrorMsg(MSG_CANT_CREATE_AREXX_PORT);
MyExit(5);
}
}
Permit();
RSignal = 1L << RexxPort->mp_SigBit;
/* open up the timer */
if (!(TimerPort = CreatePort(NULL, 0))) {
ErrorMsg(MSG_CANT_CREATE_TIMER_PORT);
MyExit(5);
}
if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) & TimerIO, 0)) {
ErrorMsg(MSG_COULDNT_OPEN, TIMERNAME);
MyExit(5);
}
TimerIO.tr_node.io_Message.mn_ReplyPort = TimerPort;
TSignal = 1L << TimerPort->mp_SigBit;
TimerUP = TRUE;
if ((NotifySignal = AllocSignal(-1)) == -1) {
ErrorMsg(MSG_COULDNT_ALLOC_NOTIFY_SIG);
MyExit(5);
}
NSignal = 1L << NotifySignal;
memset((char *) &MyNotifyRequest, 0, sizeof(struct NotifyRequest));
MyNotifyRequest.nr_Name = CronTabName;
MyNotifyRequest.nr_Flags = NRF_SEND_SIGNAL;
MyNotifyRequest.nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
MyNotifyRequest.nr_stuff.nr_Signal.nr_SignalNum = NotifySignal;
if (StartNotify(&MyNotifyRequest) == DOSFALSE) {
ErrorMsg(MSG_COULDNT_START_NOTIFY);
MyExit(5);
}
NotifyUP = TRUE;
ReadCronTab();
/* initialize jobQueue Maximums */
for (index = 0; index < 27; index++) {
jobQueue[index].jq_Max = 1;
jobQueue[index].jq_Cur = 0;
NewList((struct List *) & jobQueue[index].jq_FIFOList);
}
/*
* set queue 0 to 0xFFFFFFFF (infinite) so that jobs with no queue
* specified will use one with no limits
*/
jobQueue[0].jq_Max = 0xFFFFFFFF;
/* print a banner to the world saying we've started */
if (!WBMsg) {
sprintf(TextBuf, GetString(&LocaleInfo, MSG_DAEMON_STARTED), CYBERCRON, VersionID);
PutStr(TextBuf);
PutStr("\n");
PutStr(GetString(&LocaleInfo, MSG_COPYRIGHT));
Flush(Output());
}
Log(MSG_DAEMON_STARTED, CYBERCRON, VersionID);
Log(MSG_PORTNAME, (ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"));
ErrorsToStdErr = TRUE;
/*
* loop forever waiting for each minute and checking to see if we
* need to do anything. also look for break, notify, etc.
* BringerDown can be set by the ARexx SHUTDOWN command so check it
* as well.
*/
for (; BringerDown == FALSE;) {
TimerIO.tr_node.io_Command = TR_ADDREQUEST;
TimerIO.tr_time.tv_micro = 0;
GetSysTime(&tr_time);
TimerIO.tr_time.tv_secs = 60 - tr_time.tv_secs % 60;
SetSignal(0L, TSignal);
SendIO((struct IORequest *) & TimerIO);
DoingTimeRequest = TRUE;
signals = Wait(TSignal | NSignal | RSignal | SIGBREAKF_CTRL_C);
if (signals & TSignal) {
GetMsg(TimerPort);
DoingTimeRequest = FALSE;
if (Suspended == FALSE)
ScanForJobs();
}
if (signals & NSignal)
if (lock = Lock(CronTabName, ACCESS_READ)) {
FreeEvents(TRUE);
/*
* not really an error, but ReadCronTab()
* will send messages along the lines of
* parse error in line # if they occur so we
* spit this out to let them know why they
* are getting these parse errors.
*/
ErrorMsg(MSG_CRONTAB_MODIFIED);
Log(MSG_CRONTAB_MODIFIED);
ReadCronTab();
UnLock(lock);
}
if (signals & RSignal)
HandleRexxEvents();
if (DoingTimeRequest) {
DoingTimeRequest = FALSE;
AbortIO((struct IORequest *) & TimerIO);
WaitIO((struct IORequest *) & TimerIO);
}
if (signals & SIGBREAKF_CTRL_C)
break;
}
BringerDown = TRUE;
ObtainSemaphore(&jobSema->mps_Sema);
numJobs = NumSystemJobs + NumARexxJobs;
ReleaseSemaphore(&jobSema->mps_Sema);
if (numJobs) {
ErrorMsg(MSG_WAITING_TO_QUIT);
for (; numJobs;) {
TimerIO.tr_node.io_Command = TR_ADDREQUEST;
TimerIO.tr_time.tv_secs = 15;
TimerIO.tr_time.tv_micro = 0;
SetSignal(0L, TSignal);
SendIO((struct IORequest *) & TimerIO);
DoingTimeRequest = TRUE;
signals = Wait(TSignal | RSignal);
if (signals & TSignal) {
GetMsg(TimerPort);
DoingTimeRequest = FALSE;
}
if (signals & RSignal)
HandleRexxEvents();
if (DoingTimeRequest) {
DoingTimeRequest = FALSE;
AbortIO((struct IORequest *) & TimerIO);
WaitIO((struct IORequest *) & TimerIO);
}
ObtainSemaphore(&jobSema->mps_Sema);
numJobs = NumSystemJobs + NumARexxJobs;
ReleaseSemaphore(&jobSema->mps_Sema);
}
}
MyExit(0);
}
/* loop through the event list looking for any jobs to start */
void ScanForJobs(void)
{
struct CyberNode *cn, *tcn;
UBYTE DayOfWeek;
UWORD Month;
ULONG Day;
ULONG Hour;
ULONG Min;
BOOL TimeMatch;
SystemTime_t st;
static ULONG LastScan = 0;
GetSystemTime(&st, FALSE);
/*
* figure out if the time has gone backwards. This could happen if
* the clock was reloaded, etc. We use a 5 minute threshhold. If it
* goes back farther than that then we assume the user knows what
* they are doing.
*/
{
ULONG MSSC = st.st_tvsecs / 60; /* MSSC stands for Minutes
* Since System Creation */
if (MSSC >= (LastScan - 5) && MSSC <= LastScan)
return;
else
LastScan = MSSC;
}
/* initilize the bit fields for us to do comparisons against */
DayOfWeek = 1L << st.st_DOW;
Month = 1L << st.st_Month;
Day = 1L << st.st_Day;
Hour = 1L << st.st_Hour;
if (st.st_Min > 31)
Min = 1L << (st.st_Min - 32);
else
Min = 1L << st.st_Min;
/* loop through the list looking for events to do */
for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
TimeMatch = FALSE;
if (cn->cn_DayOfWeek & DayOfWeek)
if (cn->cn_Month & Month)
if (cn->cn_Day & Day)
if (cn->cn_Hour & Hour)
if ((st.st_Min > 31 && cn->cn_HiMin & Min) ||
(st.st_Min < 32 && cn->cn_LoMin & Min))
TimeMatch = TRUE;
ObtainSemaphore(&jobSema->mps_Sema);
if (TimeMatch || cn->cn_DelayedCount) {
if (ProcessQueue(cn, TimeMatch)) {
if (cn->cn_Flags & CNF_REXX)
StartRexxJob(cn);
else
StartSystemJob(cn);
if (cn->cn_Flags & CNF_EXECONE) {
tcn = (struct CyberNode *) cn->cn_Node.ln_Pred;
DeleteEvent(cn);
cn = tcn;
}
}
}
ReleaseSemaphore(&jobSema->mps_Sema);
}
}
/*
* this routine will decide if we can run the job now or if it is to be
* delayed because we are currently at the maximum number of jobs allowed
* for the specified queue. it serializes the delayed jobs on the queue by
* using a fifo mechanism which is maintained here
*/
BOOL ProcessQueue(struct CyberNode * cn, BOOL TimeMatch)
{
struct QueueFIFO *qf;
BOOL RetVal;
BOOL AddFIFO;
AddFIFO = RetVal = FALSE;
if (jobQueue[cn->cn_ObeyQueue].jq_Cur >= jobQueue[cn->cn_ObeyQueue].jq_Max) {
if (TimeMatch)
AddFIFO = TRUE;
else
return FALSE;
}
if (!AddFIFO) {
if (cn->cn_DelayedCount) {
qf = (struct QueueFIFO *) RemHead((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList);
if (qf->qf_CyberNode == cn) {
RetVal = TRUE;
cn->cn_DelayedCount--;
FreeVec(qf);
}
else {
AddHead((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList, (struct Node *) qf);
}
/*
* if we were delayed but we would also start a job
* at this time anyway, we need to queue a new job to
* occur
*/
if (TimeMatch)
AddFIFO = TRUE;
}
else if (TimeMatch)
RetVal = TRUE;
}
if (AddFIFO) {
if (qf = (struct QueueFIFO *) AllocVec(sizeof(struct QueueFIFO), MEMF_CLEAR)) {
qf->qf_CyberNode = cn;
AddTail((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList, (struct Node *) qf);
cn->cn_DelayedCount++;
}
}
return RetVal;
}
void HandleRexxEvents(void)
{
struct RexxMsg *msg;
/* this is the table of ARexx commands that CyberCron knows. the
format is as follows:
1) a short that is the length of the command name
2) a pointer to the command name
3) a short to descibe the args to pass to the function.
value 0 = no args
value 1 = pointer to string after command name
value 2 = an integer
value 3 = pointer to the current ARexx message
4) a short to describe the return value from the function
value 0 = no returns, set rc to zero
value 1 = return an argstring
value 2 = return integer in rc
value 3 = return an argstring already in argstring format
5) a pointer to the function
*/
#define NUMRXCMDS 22
static struct {
short len;
char *RxCmd;
short args;
short ret;
APTR func;
} CmdTbl[NUMRXCMDS] = {
/* indent makes this look so ugly... sigh... */
{
8, "SHUTDOWN", 0, 0, (APTR) & rx_Shutdown
},
{
4, "QUIT", 0, 0, (APTR) & rx_Shutdown
},
{
7, "VERSION", 0, 1, (APTR) & rx_Version
},
{
7, "SUSPEND", 0, 0, (APTR) & rx_Suspend
},
{
6, "RESUME", 0, 0, (APTR) & rx_Resume
},
{
14, "NEW_EVENT_FILE", 1, 2, (APTR) & rx_NewEventFile
},
{
16, "CLOSE_EVENT_FILE", 0, 0, (APTR) & rx_CloseEventFile
},
{
9, "ADD_EVENT", 1, 2, (APTR) & rx_AddEvent
},
{
11, "SHOW_STATUS", 0, 1, (APTR) & rx_ShowStatus
},
{
17, "PURGE_REXX_EVENTS", 0, 0, (APTR) & rx_PurgeRexxEvents
},
{
17, "DELETE_REXX_EVENT", 1, 2, (APTR) & rx_DeleteRexxEvent
},
{
12, "DELETE_EVENT", 1, 2, (APTR) & rx_DeleteEvent
},
{
11, "LIST_EVENTS", 0, 3, (APTR) & rx_ListEvents
},
{
10, "SHOW_EVENT", 1, 1, (APTR) & rx_ShowEvent
},
{
12, "NEW_LOG_FILE", 1, 2, (APTR) & rx_NewLogFile
},
{
14, "CLOSE_LOG_FILE", 0, 0, (APTR) & rx_CloseLogFile
},
{
13, "SET_QUEUE_MAX", 1, 2, (APTR) & rx_SetQueueMax
},
{
13, "GET_QUEUE_MAX", 1, 2, (APTR) & rx_GetQueueMax
},
{
15, "EVENT_NEXT_EXEC", 1, 1, (APTR) & rx_EventNextExec
},
{
18, "NEXT_EVENT_TO_EXEC", 0, 1, (APTR) & rx_NextEventToExec
},
{
11, "EXPAND_SSSC", 2, 1, (APTR) & rx_ExpandSSSC
},
{
13, "SSSC_TO_ASCII", 2, 1, (APTR) & rx_SSSCtoASCII
}
};
/*
* if we can't get ahold of the rexx system library then we spin
* emptying our message port by replying to everything. shouldn't
* happen, but if some idiot tries sending us messages when they
* don't have ARexx then its better safe than sorry
*/
if (GetARexxLib() == FALSE) {
ErrorMsg(MSG_CANT_HANDLE_REXX_EVENT, RXSNAME);
Log(MSG_CANT_HANDLE_REXX_EVENT, RXSNAME);
while ((msg = (struct RexxMsg *) GetMsg(RexxPort)))
ReplyMsg((struct Message *) msg);
return;
}
/*
* we've got the ARexx library so spin on our port looking for
* messages. if its a reply then a command/string we launched has
* finished and ARexx is returning its results to us. Otherwise,
* it's a command we are to execute so call DoMsg() to dispatch it
*/
while ((msg = (struct RexxMsg *) GetMsg(RexxPort)))
if (msg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
if (!msg->rm_Args[3])
Log(MSG_JOB_ENDED, (UWORD) msg->rm_Args[2]);
/*
* when a system job can't find the command it wanted
* to execute, the shell automatically prints out
* that it couldn't find it or whatever the error
* might happen to be. Thus, the user is informed of
* this based on wherever they may have redirected
* output. ARexx, on the other hand, does not do
* this for us. So here I have duplicated what the RX
* activator prints out when it can't start a job.
* Note that ARexxErrorMsg() is a hack defined in
* CyberCron.h since this isn't normally available
* from the standard C= includes
*/
if (msg->rm_Result1)
if (msg->rm_Result2)
FPrintf(msg->rm_Stdout, GetString(&LocaleInfo, MSG_AREXX_RETURNED_2), msg->rm_Result1,
msg->rm_Result2, (ARexxErrorMsg(msg->rm_Result2))->ns_Buff);
else
FPrintf(msg->rm_Stdout, GetString(&LocaleInfo, MSG_AREXX_RETURNED_1), msg->rm_Result1);
Close(msg->rm_Stdout);
Close(msg->rm_Stdin);
FreeJobNum((UWORD) msg->rm_Args[2]);
ObtainSemaphore(&jobSema->mps_Sema);
jobQueue[(int) msg->rm_Args[4]].jq_Cur--;
ReleaseSemaphore(&jobSema->mps_Sema);
if (msg->rm_Args[1])
DeleteArgstring(msg->rm_Args[1]);
DeleteArgstring(msg->rm_Args[0]);
DeleteRexxMsg(msg);
NumARexxJobs--;
}
else
DoMsg(msg, (APTR) & CmdTbl, NUMRXCMDS, BringerDown);
FreeARexxLib();
}
void rx_Shutdown(void)
{
BringerDown = TRUE;
}
STRPTR rx_Version(void)
{
return VersionID;
}
void rx_Suspend(void)
{
Suspended = TRUE;
}
void rx_Resume(void)
{
Suspended = FALSE;
}
int rx_NewEventFile(STRPTR name)
{
BPTR lock;
if (lock = Lock(name, ACCESS_READ)) {
if (strlen(name) + 1 > sizeof(CronTabName)) {
ErrorMsg(MSG_CRONTAB_NAME_TOO_LONG);
UnLock(lock);
return RC_WARN;
}
FreeEvents(TRUE);
if (NotifyUP) {
EndNotify(&MyNotifyRequest);
NotifyUP = FALSE;
}
strcpy(CronTabName, name);
memset((char *) &MyNotifyRequest, 0, sizeof(struct NotifyRequest));
MyNotifyRequest.nr_Name = CronTabName;
MyNotifyRequest.nr_Flags = NRF_SEND_SIGNAL;
MyNotifyRequest.nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
MyNotifyRequest.nr_stuff.nr_Signal.nr_SignalNum = NotifySignal;
if (StartNotify(&MyNotifyRequest) == DOSFALSE) {
ErrorMsg(MSG_COULDNT_START_NOTIFY);
UnLock(lock);
strcpy(CronTabName, "<None>");
return RC_ERROR;
}
NotifyUP = TRUE;
/* again, not really an error */
ErrorMsg(MSG_CRONTAB_CHANGED);
Log(MSG_CRONTAB_CHANGED);
ReadCronTab();
UnLock(lock);
return RC_OK;
}
return RC_WARN;
}
void rx_CloseEventFile(void)
{
FreeEvents(TRUE);
EndNotify(&MyNotifyRequest);
NotifyUP = FALSE;
strcpy(CronTabName, "<None>");
}
int rx_NewLogFile(STRPTR name)
{
if (strlen(name) + 1 > sizeof(LogFile)) {
ErrorMsg(MSG_LOGFILE_NAME_TOO_LONG);
return RC_WARN;
}
ObtainSemaphore(&logSema->mps_Sema);
strcpy(LogFile, name);
ReleaseSemaphore(&logSema->mps_Sema);
return RC_OK;
}
void rx_CloseLogFile(void)
{
ObtainSemaphore(&logSema->mps_Sema);
LogFile[0] = '\0';
ReleaseSemaphore(&logSema->mps_Sema);
}
STRPTR rx_ShowStatus(void)
{
sprintf(BigBufOne, "%s \"%s\" \"%s\"",
(Suspended ? "SUSPENDED" : "ACTIVE"), CronTabName,
(LogFile[0] ? LogFile : (UBYTE *) "<None>"));
return BigBufOne;
}
void rx_PurgeRexxEvents(void)
{
FreeEvents(FALSE);
}
int rx_AddEvent(STRPTR event)
{
struct CyberNode *cn;
if (cn = ParseEvent(event)) {
AddTail(&EventList, (struct Node *) cn);
return RC_OK;
}
else
return RC_ERROR;
}
int rx_DeleteRexxEvent(STRPTR name)
{
struct CyberNode *cn;
if ((cn = FindEvent(name)) && !(cn->cn_Flags & CNF_CRONTAB)) {
DeleteEvent(cn);
return RC_OK;
}
return RC_ERROR;
}
int rx_DeleteEvent(STRPTR name)
{
struct CyberNode *cn;
if (cn = FindEvent(name)) {
DeleteEvent(cn);
return RC_OK;
}
return RC_ERROR;
}
STRPTR rx_ListEvents(void)
{
struct CyberNode *cn;
STRPTR string, string2;
ULONG num;
num = 0;
for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
cn = (struct CyberNode *) cn->cn_Node.ln_Succ)
num++;
if (num == 0)
return CreateArgstring("<None>", 6);
if (!(string = CreateArgstring(NULL, num * 11)))
return NULL;
string2 = string;
for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
sprintf(string2, "0x%08lx ", cn);
string2 += 11;
}
*--string2 = '\0';
return string;
}
#define rxSE_Buf BigBufOne
STRPTR rx_ShowEvent(STRPTR name)
{
struct CyberNode *cn;
STRPTR ptr;
ULONG Bits[2];
if (!(cn = FindEvent(name)))
return NULL;
sprintf(rxSE_Buf, "0x%08lx ", cn);
ptr = &rxSE_Buf[11];
if (cn->cn_Name) {
sprintf(ptr, ":NAME %s ", cn->cn_Name);
ptr += strlen(ptr);
}
Bits[0] = cn->cn_LoMin, Bits[1] = cn->cn_HiMin;
UnParseBits(Bits, ptr, 0, 59);
ptr += strlen(ptr);
*ptr++ = ' ';
Bits[1] = 0;
Bits[0] = cn->cn_Hour;
UnParseBits(Bits, ptr, 0, 23);
ptr += strlen(ptr);
*ptr++ = ' ';
Bits[0] = cn->cn_Day;
UnParseBits(Bits, ptr, 1, 31);
ptr += strlen(ptr);
*ptr++ = ' ';
Bits[0] = cn->cn_Month;
UnParseBits(Bits, ptr, 1, 12);
ptr += strlen(ptr);
*ptr++ = ' ';
Bits[0] = cn->cn_DayOfWeek;
UnParseBits(Bits, ptr, 0, 6);
ptr += strlen(ptr);
*ptr++ = ' ';
if (cn->cn_Flags & CNF_EXECONE) {
strcpy(ptr, ":EXECONCE ");
ptr += 10;
}
if (cn->cn_Flags & CNF_NOLOG) {
strcpy(ptr, ":NOLOG ");
ptr += 7;
}
if (cn->cn_Flags & CNF_REXX) {
strcpy(ptr, ":REXX ");
ptr += 6;
}
else {
if (cn->cn_Stack != DefaultStackSize) {
sprintf(ptr, ":STACK %ld ", cn->cn_Stack);
ptr += strlen(ptr);
}
if (cn->cn_Priority != DefaultPriority) {
sprintf(ptr, ":PRI %ld ", cn->cn_Priority);
ptr += strlen(ptr);
}
if (cn->cn_CustomShell) {
sprintf(ptr, ":CUSTOMSH %s ", cn->cn_CustomShell);
ptr += strlen(ptr);
}
else if (cn->cn_Flags & CNF_SYSSH) {
strcpy(ptr, ":SYSSH ");
ptr += 7;
}
}
strcpy(ptr, cn->cn_Command);
ptr += strlen(ptr);
*ptr++ = ' ';
if (cn->cn_Args) {
strcpy(ptr, cn->cn_Args);
ptr += strlen(ptr);
*ptr++ = ' ';
}
if (cn->cn_ReDirIn) {
sprintf(ptr, "< %s ", cn->cn_ReDirIn);
ptr += strlen(ptr);
}
if (cn->cn_SendToUser && SendMailCmd[0]) {
sprintf(ptr, ":MAILUSER %s ", cn->cn_SendToUser);
ptr += strlen(ptr);
}
else {
if (cn->cn_ReDirOut) {
sprintf(ptr, "%s %s ", (cn->cn_Flags & CNF_OUTAPP ? ">>" : ">"), cn->cn_ReDirOut);
ptr += strlen(ptr);
}
}
if (cn->cn_ReDirErr) {
sprintf(ptr, "2%s %s ", (cn->cn_Flags & CNF_ERRAPP ? ">>" : ">"), cn->cn_ReDirErr);
ptr += strlen(ptr);
}
if (cn->cn_ObeyQueue) {
sprintf(ptr, ":OBEYQUEUE %lc ", cn->cn_ObeyQueue + 'a' - 1);
ptr += 13;
}
*--ptr = '\0';
return rxSE_Buf;
}
int rx_SetQueueMax(STRPTR argline)
{
int queueNum;
if (isalpha(*argline)) {
queueNum = tolower(*argline) - 'a' + 1;
argline++;
while (isspace(*argline++)) ;
if (*--argline) {
jobQueue[queueNum].jq_Max = atol(argline);
return RC_OK;
}
}
return RC_ERROR;
}
int rx_GetQueueMax(STRPTR argline)
{
int queueNum;
if (isalpha(*argline)) {
queueNum = tolower(*argline) - 'a' + 1;
return (int) jobQueue[queueNum].jq_Max;
}
else
return -1;
}
STRPTR rx_EventNextExec(STRPTR name)
{
struct CyberNode *cn;
SystemTime_t st;
if (!(cn = FindEvent(name)))
return NULL;
GetSystemTime(&st, FALSE);
sprintf(BigBufOne, "%ld", EventNextExecutes(cn, &st));
return BigBufOne;
}
STRPTR rx_NextEventToExec(void)
{
struct CyberNode *cn, *bestcn;
ULONG secs, bestsecs;
SystemTime_t st;
GetSystemTime(&st, FALSE);
bestcn = (struct CyberNode *) NULL;
bestsecs = -1;
for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
secs = EventNextExecutes(cn, &st);
if (secs && secs < bestsecs) {
bestcn = cn;
bestsecs = secs;
}
}
if (bestcn) {
sprintf(BigBufOne, "0x%08lx %ld", bestcn, bestsecs);
return BigBufOne;
}
else
return "<None>";
}
STRPTR rx_ExpandSSSC(int SSSC)
{
SystemTime_t st;
st.st_tvsecs = SSSC;
GetSystemTime(&st, TRUE);
sprintf(BigBufOne, "%ld %ld %ld %ld %ld %ld %ld", st.st_Sec, st.st_Min,
st.st_Hour, st.st_Day, st.st_Month, st.st_Year, st.st_DOW);
return BigBufOne;
}
STRPTR rx_SSSCtoASCII(int SSSC)
{
struct DateTime dat;
UBYTE Day[LEN_DATSTRING + 1];
UBYTE Date[LEN_DATSTRING + 1];
UBYTE Time[LEN_DATSTRING + 1];
dat.dat_Stamp.ds_Days = SSSC / SECSINDAY, SSSC %= SECSINDAY;
dat.dat_Stamp.ds_Minute = SSSC / SECSINMINUTE, SSSC %= SECSINMINUTE;
dat.dat_Stamp.ds_Tick = SSSC * TICKS_PER_SECOND;
dat.dat_Format = FORMAT_DOS;
dat.dat_Flags = 0;
dat.dat_StrDay = Day;
dat.dat_StrDate = Date;
dat.dat_StrTime = Time;
memset(Day, 0, LEN_DATSTRING + 1);
memset(Date, 0, LEN_DATSTRING + 1);
memset(Time, 0, LEN_DATSTRING + 1);
DateToStr(&dat);
sprintf(BigBufOne, "%s %s %s", Day, Date, Time);
return BigBufOne;
}
STRPTR WBtoCLIargs(struct WBStartup * WBMsg, STRPTR ArgTemplate)
{
UBYTE *Argline, *ArglineSave, *SourcePtr, *DestPtr;
struct Library *IconBase;
UBYTE tempChar;
BOOL sawEqual;
int index;
ULONG size;
BPTR oldDir;
struct DiskObject *dob;
if (!(IconBase = OpenLibrary("icon.library", 37)))
return NULL;
oldDir = CurrentDir(WBMsg->sm_ArgList[0].wa_Lock);
if (!(dob = GetDiskObjectNew(WBMsg->sm_ArgList[0].wa_Name))) {
CurrentDir(oldDir);
CloseLibrary(IconBase);
return NULL;
}
/*
* if there are tooltypes then figure out how much memory we need to
* allocate to hold them as a command line. anything that isn't a
* legal argument in our command line template is ignored. This lets
* things like DONOTWAIT be used without causing an error at
* ReadArgs() time.
*/
if (dob->do_ToolTypes)
for (size = index = 0; dob->do_ToolTypes[index]; index++) {
SourcePtr = dob->do_ToolTypes[index];
DestPtr = BigBufOne;
while (tempChar = *SourcePtr++)
if (tempChar == '=')
break;
else
*DestPtr++ = tempChar;
*DestPtr = '\0';
if (FindArg(ArgTemplate, BigBufOne) != -1)
size += strlen(dob->do_ToolTypes[index]) + 3;
}
else
size = 0;
if (Argline = AllocVec(size + 2, MEMF_CLEAR)) {
ArglineSave = Argline;
if (dob->do_ToolTypes)
for (index = 0; dob->do_ToolTypes[index]; index++) {
SourcePtr = dob->do_ToolTypes[index];
DestPtr = BigBufOne;
sawEqual = FALSE;
while (tempChar = *SourcePtr++)
if (tempChar == '=') {
sawEqual = TRUE;
break;
}
else
*DestPtr++ = tempChar;
*DestPtr = '\0';
if (FindArg(ArgTemplate, BigBufOne) != -1) {
CopyMem(BigBufOne, Argline, DestPtr - BigBufOne);
Argline += DestPtr - BigBufOne;
/*
* if we saw an equals sign when we
* broke the argument name out then
* we know we need to copy the
* argument's data over. if we
* didn't see it then this argument
* is a switch and thus has no more
* data to copy over
*/
if (sawEqual) {
*Argline++ = ' ';
*Argline++ = '\"';
while (tempChar = *SourcePtr++)
*Argline++ = tempChar;
*Argline++ = '\"';
}
*Argline++ = ' ';
}
}
*Argline++ = '\n';
*Argline = '\0';
}
else
ArglineSave = NULL;
if (dob->do_StackSize)
DefaultStackSize = dob->do_StackSize;
FreeDiskObject(dob);
CurrentDir(oldDir);
CloseLibrary(IconBase);
return ArglineSave;
}
void MyExit(int error)
{
if (OldPriority != -1)
SetTaskPri(FindTask(NULL), OldPriority);
if (ARexxLibCount)
CloseLibrary((struct Library *) RexxSysBase);
FreeEvents(TRUE);
FreeEvents(FALSE);
if (NotifyUP)
EndNotify(&MyNotifyRequest);
FreeSignal(NotifySignal);
if (TimerUP) {
if (DoingTimeRequest) {
AbortIO((struct IORequest *) & TimerIO);
WaitIO((struct IORequest *) & TimerIO);
}
CloseDevice((struct IORequest *) & TimerIO);
}
if (TimerPort)
DeletePort(TimerPort);
if (RexxPort)
DeletePort(RexxPort);
if (ArgsPtr)
FreeArgs(ArgsPtr);
if (MyArgs)
FreeDosObject(DOS_RDARGS, MyArgs);
if (logSema)
FreeMyPublicSemaphore(logSema);
if (jobSema)
FreeMyPublicSemaphore(jobSema);
CloseLibrary(UtilityBase);
CloseLibrary(OwnDevUnitBase);
FreeVec(WBArgs);
if (LocaleInfo.li_LocaleBase) {
CloseCatalog(LocaleInfo.li_Catalog);
CloseLibrary((struct Library *) LocaleInfo.li_LocaleBase);
}
((struct Process *) FindTask(NULL))->pr_WindowPtr = OldWindowPtr;
XCEXIT(error);
}
/*
* this routine will read the crontab file, calling ParseEvent() to create
* CyberNodes, and then link them into the event list.
*
*/
#define RCT_Buf BigBufOne
void ReadCronTab(void)
{
BPTR fh;
struct CyberNode *cn;
ULONG line = 0;
LONG error;
if (!(fh = Open(CronTabName, MODE_OLDFILE))) {
ErrorMsg(MSG_OPENING_CRONTAB, CronTabName);
Log(MSG_OPENING_CRONTAB, CronTabName);
return;
}
while (FGets(fh, RCT_Buf, BIG_BUF_SIZE_BASE)) {
line++;
if (RCT_Buf[0] == '#' || RCT_Buf[0] == '\n')
continue;
if (cn = ParseEvent(RCT_Buf)) {
cn->cn_Flags |= CNF_CRONTAB;
AddTail(&EventList, (struct Node *) cn);
}
else {
ErrorMsg(MSG_PARSING, line, CronTabName);
Log(MSG_PARSING, line, CronTabName);
}
}
error = IoErr();
if (error) {
Fault(error, NULL, RCT_Buf, BIG_BUF_SIZE_BASE);
ErrorMsg(MSG_IO_FAULT_IN_CRONTAB, error, RCT_Buf, line + 1, CronTabName);
Log(MSG_IO_FAULT_IN_CRONTAB, error, RCT_Buf, line + 1, CronTabName);
}
Close(fh);
}
/*
* this routine will parse an ASCII string and make a CyberNode out of it.
* it returns NULL if there was an error during the parse. otherwise it
* returns a pointer to the node.
*
* note that we do something really sneaky here. we use the ReadArgs() routine
* to do the initial parse!. This means that the order in which items occur
* in a crontab entry can be virtually anything the user desires!
*/
#define PE_TEMPLATE "Event/M,</K,>/K,>>/K,2>/K,2>>/K,:NAME/K,:STACK/K/N,:PRI/K/N,:CUSTOMSH/K,:MAILUSER/K,:OBEYQUEUE/K,:SYSSH/S,:REXX/S,:NOLOG/S,:EXECONCE/S"
enum ParseEventReadArgs {
PEARG_EVENT,
PEARG_REDIRIN,
PEARG_REDIROUT,
PEARG_REDIROUT2,
PEARG_REDIRERR,
PEARG_REDIRERR2,
PEARG_REXXNAME,
PEARG_STACK,
PEARG_PRI,
PEARG_CUSTOMSH,
PEARG_MAILUSER,
PEARG_OBEYQUEUE,
PEARG_SYSSH,
PEARG_REXX,
PEARG_NOLOG,
PEARG_EXECONE,
PEARG_sizeof
};
enum Events {
EVENT_MINUTE,
EVENT_HOUR,
EVENT_DAY,
EVENT_MONTH,
EVENT_DOW,
EVENT_COMMAND,
EVENT_ARGS
};
#define PE_Buf BigBufTwo
struct CyberNode *ParseEvent(STRPTR event)
{
struct CyberNode *cn;
struct RDArgs *PArgsPtr;
struct RDArgs *PMyArgs;
char *ArgArray[PEARG_sizeof];
register char **EventArgs;
register int index;
ULONG size;
ULONG Bits[2];
/* allocate our RDArgs structure */
if (!(PMyArgs = (struct RDArgs *) AllocDosObject(DOS_RDARGS, TAG_DONE)))
return (struct CyberNode *) NULL;
PMyArgs->RDA_Flags |= RDAF_NOPROMPT;
/*
* set up the buffer for our ReadArgs() call. We have to copy over
* the string and put a new line at the end of it because of a
* limitation of ReadArgs(). sigh.
*/
{
ULONG length;
length = strlen(event);
if (length + 2 > sizeof(PE_Buf)) {
FreeDosObject(DOS_RDARGS, PMyArgs);
return (struct CyberNode *) NULL;
}
CopyMem(event, PE_Buf, length);
PE_Buf[length++] = '\n';
PE_Buf[length] = '\0';
PMyArgs->RDA_Source.CS_Buffer = PE_Buf;
PMyArgs->RDA_Source.CS_Length = length;
PMyArgs->RDA_Source.CS_CurChr = 0L;
}
/*
* here we walk through the event line to make sure it isnt all
* blank.
*/
while (isspace(*event))
event++;
if (!*event) {
FreeDosObject(DOS_RDARGS, PMyArgs);
return (struct CyberNode *) NULL;
}
memset(ArgArray, 0, sizeof(ArgArray));
/* now we call ReadArgs() */
PArgsPtr = ReadArgs(PE_TEMPLATE, (LONG *) & ArgArray, PMyArgs);
if (!PArgsPtr) {
FreeDosObject(DOS_RDARGS, PMyArgs);
return (struct CyberNode *) NULL;
}
/*
* if they specified a name to be known as via the rexx port, make
* sure it doesn't start with 0x because that's what we use to prefix
* a hex number for nodes with no name and we don't want the user
* fooling around with names we consider private.
*/
if (ArgArray[PEARG_REXXNAME])
if (ArgArray[PEARG_REXXNAME][0] == '0' && tolower(ArgArray[PEARG_REXXNAME][1]) == 'x') {
FreeArgs(PArgsPtr);
FreeDosObject(DOS_RDARGS, PMyArgs);
return (struct CyberNode *) NULL;
}
/*
* ok, ReadArgs has parsed the event for us. make sure that we have
* at least 5 time specs and a command name.
*/
EventArgs = (char **) ArgArray[PEARG_EVENT];
for (index = EVENT_MINUTE; index < EVENT_COMMAND; index++, EventArgs++)
if (!*EventArgs || !isdigit(*EventArgs[0]) && *EventArgs[0] != '*') {
FreeArgs(PArgsPtr);
FreeDosObject(DOS_RDARGS, PMyArgs);
return (struct CyberNode *) NULL;
}
/*
* we have the five time spec strings. now check to make sure we
* have a command name. we will also calculate its size as well as
* the size of any args for the command while we are at it
*/
if (!*EventArgs) {
FreeArgs(PArgsPtr);
FreeDosObject(DOS_RDARGS, PMyArgs);
return (struct CyberNode *) NULL;
}
size = strlen(*EventArgs++) + 1;
while (*EventArgs)
size += strlen(*EventArgs++) + 1;
/*
* now figure out the memory needed to store the other textual items
* for this node
*/
if (ArgArray[PEARG_REDIRIN])
size += strlen(ArgArray[PEARG_REDIRIN]) + 1;
if (ArgArray[PEARG_REDIROUT])
size += strlen(ArgArray[PEARG_REDIROUT]) + 1;
if (ArgArray[PEARG_REDIROUT2]) {
size += strlen(ArgArray[PEARG_REDIROUT2]) + 1;
if (ArgArray[PEARG_REDIROUT])
size -= strlen(ArgArray[PEARG_REDIROUT]) + 1;
}
if (ArgArray[PEARG_REDIRERR])
size += strlen(ArgArray[PEARG_REDIRERR]) + 1;
if (ArgArray[PEARG_REDIRERR2]) {
size += strlen(ArgArray[PEARG_REDIRERR2]) + 1;
if (ArgArray[PEARG_REDIRERR])
size -= strlen(ArgArray[PEARG_REDIRERR]) + 1;
}
if (ArgArray[PEARG_REXXNAME])
size += strlen(ArgArray[PEARG_REXXNAME]) + 1;
if (ArgArray[PEARG_CUSTOMSH])
size += strlen(ArgArray[PEARG_CUSTOMSH]) + 1;
if (ArgArray[PEARG_MAILUSER])
size += strlen(ArgArray[PEARG_MAILUSER]) + 1;
if (ArgArray[PEARG_OBEYQUEUE] && !isalpha(ArgArray[PEARG_OBEYQUEUE][0])) {
FreeArgs(PArgsPtr);
FreeDosObject(DOS_RDARGS, PMyArgs);
return (struct CyberNode *) NULL;
}
if (!(cn = (struct CyberNode *) AllocVec(size + sizeof(struct CyberNode) + 1, MEMF_CLEAR))) {
FreeArgs(PArgsPtr);
FreeDosObject(DOS_RDARGS, PMyArgs);
return (struct CyberNode *) NULL;
}
/*
* now that we have got the memory for the CyberNode start filling it
* in. we start by testing the STACK and PRI fields of the arg list
* and use Atol() to get their values if present. We then test the
* REXX and NOLOG flags and use them to set the cn_Flags element.
*/
if (ArgArray[PEARG_STACK])
cn->cn_Stack = *((LONG *) ArgArray[PEARG_STACK]);
if (cn->cn_Stack < 2048)
cn->cn_Stack = DefaultStackSize;
if (ArgArray[PEARG_PRI])
cn->cn_Priority = *((LONG *) ArgArray[PEARG_PRI]) & 0xFF;
else
cn->cn_Priority = DefaultPriority;
if (ArgArray[PEARG_OBEYQUEUE])
cn->cn_ObeyQueue = tolower(ArgArray[PEARG_OBEYQUEUE][0]) - 'a' + 1;
if (ArgArray[PEARG_REXX])
cn->cn_Flags |= CNF_REXX;
if (ArgArray[PEARG_NOLOG])
cn->cn_Flags |= CNF_NOLOG;
if (ArgArray[PEARG_SYSSH])
cn->cn_Flags |= CNF_SYSSH;
if (ArgArray[PEARG_EXECONE])
cn->cn_Flags |= CNF_EXECONE;
/*
* now prepare to copy the textual items over into memory behind the
* CyberNode
*/
event = (char *) cn + sizeof(struct CyberNode);
if (ArgArray[PEARG_REXXNAME]) {
cn->cn_Name = event;
size = strlen(ArgArray[PEARG_REXXNAME]) + 1;
CopyMem(ArgArray[PEARG_REXXNAME], event, size);
event += size;
}
if (ArgArray[PEARG_REDIRIN]) {
cn->cn_ReDirIn = event;
size = strlen(ArgArray[PEARG_REDIRIN]) + 1;
CopyMem(ArgArray[PEARG_REDIRIN], event, size);
event += size;
}
if (ArgArray[PEARG_REDIROUT] && !ArgArray[PEARG_REDIROUT2]) {
cn->cn_ReDirOut = event;
size = strlen(ArgArray[PEARG_REDIROUT]) + 1;
CopyMem(ArgArray[PEARG_REDIROUT], event, size);
event += size;
}
if (ArgArray[PEARG_REDIROUT2]) {
cn->cn_ReDirOut = event;
size = strlen(ArgArray[PEARG_REDIROUT2]) + 1;
CopyMem(ArgArray[PEARG_REDIROUT2], event, size);
event += size;
cn->cn_Flags |= CNF_OUTAPP;
}
if (ArgArray[PEARG_REDIRERR] && !ArgArray[PEARG_REDIRERR2]) {
cn->cn_ReDirErr = event;
size = strlen(ArgArray[PEARG_REDIRERR]) + 1;
CopyMem(ArgArray[PEARG_REDIRERR], event, size);
event += size;
}
if (ArgArray[PEARG_REDIRERR2]) {
cn->cn_ReDirErr = event;
size = strlen(ArgArray[PEARG_REDIRERR2]) + 1;
CopyMem(ArgArray[PEARG_REDIRERR2], event, size);
event += size;
cn->cn_Flags |= CNF_ERRAPP;
}
if (ArgArray[PEARG_CUSTOMSH]) {
cn->cn_CustomShell = event;
size = strlen(ArgArray[PEARG_CUSTOMSH]) + 1;
CopyMem(ArgArray[PEARG_CUSTOMSH], event, size);
event += size;
}
if (ArgArray[PEARG_MAILUSER]) {
cn->cn_SendToUser = event;
size = strlen(ArgArray[PEARG_MAILUSER]) + 1;
CopyMem(ArgArray[PEARG_MAILUSER], event, size);
event += size;
}
EventArgs = (char **) ArgArray[PEARG_EVENT];
cn->cn_Command = event;
index = EVENT_COMMAND;
size = strlen(EventArgs[index]);
CopyMem(EventArgs[index++], event, size);
event += size;
*event++ = 0;
if (EventArgs[index]) {
cn->cn_Args = event;
while (EventArgs[index]) {
size = strlen(EventArgs[index]);
CopyMem(EventArgs[index++], event, size);
event += size;
*event++ = ' ';
}
}
else
cn->cn_Args = NULL;
*--event = 0;
/*
* Now we need to convert the ASCII time values into bitmaps to store
* in the node. Note that we do not check to see if the time strings
* are within range or not. We simply logically AND away any invalid
* bits and use whats left.
*/
ParseBits(Bits, EventArgs[EVENT_MINUTE]);
cn->cn_LoMin = Bits[0], cn->cn_HiMin = Bits[1] & 0xFFFFFFF;
ParseBits(Bits, EventArgs[EVENT_HOUR]);
cn->cn_Hour = Bits[0] & 0xFFFFFF;
ParseBits(Bits, EventArgs[EVENT_DAY]);
cn->cn_Day = Bits[0] & 0xFFFFFFFE;
ParseBits(Bits, EventArgs[EVENT_MONTH]);
cn->cn_Month = Bits[0] & 0x1FFE;
ParseBits(Bits, EventArgs[EVENT_DOW]);
cn->cn_DayOfWeek = Bits[0] & 0x7F;
/*
* we need to see if cn_Command has any spaces in it and set
* cn_CommandHasSpace accordingly. this lets the job starters make
* some more intelligent choices.
*/
{
BOOL HasSpace;
STRPTR ptr;
HasSpace = FALSE;
for (ptr = cn->cn_Command; *ptr; ptr++)
if (isspace(*ptr)) {
HasSpace = TRUE;
break;
}
cn->cn_CommandHasSpace = HasSpace;
}
FreeArgs(PArgsPtr);
FreeDosObject(DOS_RDARGS, PMyArgs);
return cn;
}
/*
* this routine will try to figure out the next time an event would be
* executed.
*/
ULONG EventNextExecutes(struct CyberNode * cn, SystemTime_t * st)
{
ULONG days, days2;
ULONG min, hour, day, month, dow, year;
ULONG dayTarget, yearTarget;
BOOL isLeap;
for (days = -1, year = 1978; year < st->st_Year; year++)
days += LEAP(year) ? 366 : 365;
yearTarget = year + 8;
for (; year < yearTarget; year++) {
isLeap = LEAP(year);
for (month = (year == st->st_Year) ? st->st_Month : 1; month < 13; month++)
if (cn->cn_Month & (1L << month)) {
day = (month == st->st_Month && year == st->st_Year) ? st->st_Day : 1;
dayTarget = DayTable[isLeap][month - 1] + 1;
{
int i;
for (days2 = days, i = 1; i < month; i++)
days2 += DayTable[isLeap][i - 1];
}
dow = (days2 + day) % 7;
for (; day < dayTarget; day++, dow = ++dow % 7)
if ((cn->cn_DayOfWeek & (1L << dow)) && (cn->cn_Day & (1L << day)))
for (hour = (day == st->st_Day && month == st->st_Month && year == st->st_Year) ? st->st_Hour : 0; hour < 24; hour++)
if (cn->cn_Hour & (1L << hour))
for (min = (hour == st->st_Hour && day == st->st_Day && month == st->st_Month && year == st->st_Year) ? st->st_Min + 1 : 0; min < 60; min++)
if ((min > 31 && cn->cn_HiMin & (1L << (min - 32))) ||
(min < 32 && cn->cn_LoMin & (1L << min)))
return ((days2 + day) * SECSINDAY +
(hour * SECSINHOUR) +
(min * SECSINMINUTE));
}
days += isLeap ? 366 : 365;
}
/*
* if we get down here then we couldn't find anytime within a seven
* year span that satisfies the requirements to run this event. we
* return zero in this case to let the caller know that no match was
* found
*/
return 0;
}
/*
* this will take an ASCII time string and convert it into a bitmap for
* storage in a CyberNode
*/
void ParseBits(ULONG * bits, STRPTR tstr)
{
register char *ptr;
int start, end;
int save;
if (*tstr == '*') {
bits[0] = bits[1] = 0xFFFFFFFF;
return;
}
else
bits[0] = bits[1] = 0;
for (;;) {
ptr = tstr;
while (isdigit(*ptr))
ptr++;
save = *ptr, *ptr = NULL;
end = start = atol(tstr);
if (save == '-') {
tstr = ++ptr;
while (isdigit(*ptr))
ptr++;
save = *ptr, *ptr = NULL;
end = atol(tstr);
}
if (start >= 0 && end >= start)
while (start <= end) {
if (start >= 64)
break;
if (start < 32)
bits[0] |= 1L << start;
else
bits[1] |= 1L << (start - 32);
start++;
}
if (!save)
break;
else
tstr = ptr + 1;
}
}
/* convert a bit field back into an ASCII time string */
void UnParseBits(ULONG * bits, STRPTR ptr, int lowBit, int hiBit)
{
STRPTR tptr;
int curBit, startBit;
BOOL isOn, lastOn;
/* first check to see if everything is specified and return "*" if so */
for (curBit = lowBit; curBit <= hiBit; curBit++)
if ((curBit < 32 && !(bits[0] & 1L << curBit)) || (curBit > 31 && !(bits[1] & 1L << (curBit - 32))))
break;
if (curBit == hiBit + 1) {
strcpy(ptr, "*");
return;
}
/* it's not "*" so walk through and build things the hard way */
tptr = ptr;
*tptr = 0;
lastOn = FALSE;
for (curBit = lowBit; curBit < hiBit + 2; curBit++) {
if ((curBit < 32 && (bits[0] & 1L << curBit)) || (curBit > 31 && (bits[1] & 1L << (curBit - 32))))
isOn = TRUE;
else
isOn = FALSE;
if (isOn & !lastOn) {
sprintf(tptr, "%ld", curBit);
startBit = curBit;
}
if (!isOn)
if (lastOn && startBit != curBit - 1)
sprintf(tptr, "-%ld,", curBit - 1);
else if (tptr > ptr && *(tptr - 1) != ',')
strcpy(tptr, ",");
tptr += strlen(tptr);
lastOn = isOn;
}
if (tptr == ptr)
strcpy(ptr, "*"); /* Uh oh. Somehow we have a field
* with nothing specified. Fill it
* in with a "*" */
else
*--tptr = '\0';
return;
}
/* find a specific CyberNode by name */
struct CyberNode *FindEvent(STRPTR name)
{
struct CyberNode *cn;
struct CyberNode *eventAddr;
if (!name || name[0] == '\0')
return (struct CyberNode *) NULL;
if (name[0] == '0' && tolower(name[1]) == 'x')
stch_l(&name[2], (long *) &eventAddr);
else
eventAddr = 0;
for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
cn = (struct CyberNode *) cn->cn_Node.ln_Succ)
if (cn == eventAddr || (cn->cn_Name && stricmp(name, cn->cn_Name) == 0))
return cn;
return (struct CyberNode *) NULL;
}
/*
* this routine will walk through the event list and free all the nodes in
* it of a given type. If called with TRUE it will free crontab entries,
* otherwise it will free Rexx entries
*/
void FreeEvents(BOOL DoCronTabEntries)
{
register struct CyberNode *cn;
register struct CyberNode *tcn;
UBYTE Flags;
for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
Flags = cn->cn_Flags & CNF_CRONTAB;
if ((DoCronTabEntries && Flags) || (!DoCronTabEntries && !Flags)) {
tcn = (struct CyberNode *) cn->cn_Node.ln_Pred;
DeleteEvent(cn);
cn = tcn;
}
}
}
/*
* this will delete the resources used for a specific event. it checks to
* see if there are any QueueFIFO messages pending for the specified event
* and removes them as needed.
*/
void DeleteEvent(struct CyberNode * cn)
{
register struct QueueFIFO *qf;
register struct QueueFIFO *tqf;
if (cn->cn_DelayedCount) {
for (qf = (struct QueueFIFO *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList;
qf->qf_Node.mln_Succ; qf = (struct QueueFIFO *) qf->qf_Node.mln_Succ) {
if (qf->qf_CyberNode == cn) {
tqf = (struct QueueFIFO *) qf->qf_Node.mln_Pred;
Remove((struct Node *) qf);
FreeVec(qf);
qf = tqf;
}
}
}
Remove((struct Node *) cn);
FreeVec(cn);
}
/*
* this allocates an output filehandle for us which output will be piped
* through to sendmail over
*/
#define SSM_Buf BigBufOne
#define SSM_Buf2 BigBufTwo
BPTR SetupSendMail(STRPTR cmdName, STRPTR cmdArgs, STRPTR userName)
{
BPTR pfho, pfhi, ofh;
UBYTE pipeName[36];
struct timeval tr_time;
GetSysTime(&tr_time);
sprintf(pipeName, "PIPE:CyberCron.%08lx.%08lx", tr_time.tv_secs, tr_time.tv_micro);
sprintf(SSM_Buf2, GetString(&LocaleInfo, MSG_REALNAME_TEMPLATE), CYBERCRON, VersionID);
sprintf(SSM_Buf, SendMailCmd, "cybercron", SSM_Buf2);
ofh = Open("NIL:", MODE_NEWFILE);
if (!ofh)
return NULL;
pfho = Open(pipeName, MODE_NEWFILE);
if (!pfho) {
Close(ofh);
return NULL;
}
pfhi = Open(pipeName, MODE_OLDFILE);
if (!pfhi) {
Close(pfho);
Close(ofh);
return NULL;
}
if (SystemTags(SSM_Buf, SYS_Input, pfhi, SYS_Output, ofh, SYS_Asynch, TRUE,
NP_StackSize, 32768, NP_CopyVars, TRUE, NP_Cli, TRUE, TAG_DONE) == -1) {
Close(pfho);
Close(pfhi);
Close(ofh);
return NULL;
}
FPrintf(pfho, GetString(&LocaleInfo, MSG_TO_SUBJECT), userName, cmdName, (cmdArgs ? " " : NULL), cmdArgs);
Flush(pfho);
return pfho;
}
UBYTE SJ_Buf[1024 + 12];
UBYTE CN_Buf[sizeof(SJ_Buf)];
/* this routine will start up a job using System() */
void StartSystemJob(struct CyberNode * cn)
{
BPTR ifh, ofh /* , efh */ ;
struct SystemECdata *ecdata;
struct TagItem tlist[20];
int tlistIdx = 0;
if (cn->cn_Args) {
if (cn->cn_CommandHasSpace)
sprintf(SJ_Buf, "\"%s\" %s", cn->cn_Command, cn->cn_Args);
else
sprintf(SJ_Buf, "%s %s", cn->cn_Command, cn->cn_Args);
sprintf(CN_Buf, "\"»%s« %s\"", cn->cn_Command, cn->cn_Args);
}
else {
if (cn->cn_CommandHasSpace)
sprintf(SJ_Buf, "\"%s\"", cn->cn_Command);
else
strcpy(SJ_Buf, cn->cn_Command);
sprintf(CN_Buf, "\"»%s«\"", cn->cn_Command);
}
if (!(ecdata = AllocVec(sizeof(struct SystemECdata), MEMF_CLEAR)))
return;
ecdata->jobNo = GetJobNum();
if (!ecdata->jobNo) {
Log(MSG_JOB_TABLE_FULL, CN_Buf);
FreeVec(ecdata);
return;
}
if (cn->cn_ReDirIn)
ifh = Open(cn->cn_ReDirIn, MODE_OLDFILE);
else
ifh = Open("NIL:", MODE_OLDFILE);
if (!ifh) {
Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIRIN), CN_Buf);
FreeJobNum(ecdata->jobNo);
FreeVec(ecdata);
return;
}
tlist[tlistIdx].ti_Tag = SYS_Input;
tlist[tlistIdx++].ti_Data = (ULONG) ifh;
if (cn->cn_SendToUser && SendMailCmd[0])
ofh = SetupSendMail(cn->cn_Command, cn->cn_Args, cn->cn_SendToUser);
else {
if (cn->cn_ReDirOut)
ofh = Open(cn->cn_ReDirOut, (cn->cn_Flags & CNF_OUTAPP ? MODE_READWRITE : MODE_NEWFILE));
else
ofh = Open("NIL:", MODE_NEWFILE);
if (ofh && (cn->cn_Flags & CNF_OUTAPP))
Seek(ofh, 0, OFFSET_END);
}
if (!ofh) {
Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIROUT), CN_Buf);
Close(ifh);
FreeJobNum(ecdata->jobNo);
FreeVec(ecdata);
return;
}
tlist[tlistIdx].ti_Tag = SYS_Output;
tlist[tlistIdx++].ti_Data = (ULONG) ofh;
tlist[tlistIdx].ti_Tag = SYS_Asynch;
tlist[tlistIdx++].ti_Data = TRUE;
/*
Sigh.. Randell tells me that StdErr is pretty much unofficially "not for use"
under 2.0. This is here for the day that it can be used. All that should need
to be done is to pull out the comments. Oh well.
Do not uncomment this code. NP_Error and NP_ErrorClose are __IGNORED__ by the
system right now so if you specify stderr redirection, it will open the file and never
close it.
if (cn->cn_ReDirErr) {
efh = Open(cn->cn_ReDirErr, (cn->cn_Flags & CNF_ERRAPP ? MODE_READWRITE : MODE_NEWFILE));
if (!efh) {
Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIRERR), CN_Buf);
Close(ofh);
Close(ifh);
FreeJobNum(ecdata->jobNo);
FreeVec(ecdata);
return;
}
if (cn->cn_Flags & CNF_ERRAPP)
Seek(efh, 0, OFFSET_END);
tlist[tlistIdx].ti_Tag = NP_Error;
tlist[tlistIdx++].ti_Data = (ULONG)efh;
tlist[tlistIdx].ti_Tag = NP_CloseError;
tlist[tlistIdx++].ti_Data = TRUE;
} else {
tlist[tlistIdx].ti_Tag = NP_Error;
tlist[tlistIdx++].ti_Data = (ULONG)StdErr;
tlist[tlistIdx].ti_Tag = NP_CloseError;
tlist[tlistIdx++].ti_Data = FALSE;
}
*/
tlist[tlistIdx].ti_Tag = NP_StackSize;
tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_Stack;
tlist[tlistIdx].ti_Tag = NP_Priority;
tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_Priority;
if (cn->cn_CustomShell) {
tlist[tlistIdx].ti_Tag = SYS_CustomShell;
tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_CustomShell;
}
else {
tlist[tlistIdx].ti_Tag = SYS_UserShell;
tlist[tlistIdx++].ti_Data = ((cn->cn_Flags & CNF_SYSSH) ? FALSE : TRUE);
}
tlist[tlistIdx].ti_Tag = NP_Cli;
tlist[tlistIdx++].ti_Data = TRUE;
tlist[tlistIdx].ti_Tag = NP_CopyVars;
tlist[tlistIdx++].ti_Data = TRUE;
tlist[tlistIdx].ti_Tag = NP_ExitCode;
tlist[tlistIdx++].ti_Data = (ULONG) & EndSystemJob;
tlist[tlistIdx].ti_Tag = NP_ExitData;
tlist[tlistIdx++].ti_Data = (ULONG) ecdata;
tlist[tlistIdx].ti_Tag = TAG_DONE;
tlist[tlistIdx].ti_Data = 0;
ecdata->queueNo = cn->cn_ObeyQueue;
ecdata->flags = cn->cn_Flags & CNF_NOLOG;
if (SystemTagList(SJ_Buf, tlist) == -1) {
Log(MSG_COULDNT_START_SYSTEM_JOB, CN_Buf);
/*
See above for why this is currently commented out.
if (cn->cn_ReDirErr)
Close(efh);
*/
Close(ofh);
Close(ifh);
FreeJobNum(ecdata->jobNo);
FreeVec(ecdata);
return;
}
if (!(cn->cn_Flags & CNF_NOLOG))
Log(MSG_JOB_STARTED, ecdata->jobNo, CN_Buf);
ObtainSemaphore(&jobSema->mps_Sema);
NumSystemJobs++;
jobQueue[cn->cn_ObeyQueue].jq_Cur++;
ReleaseSemaphore(&jobSema->mps_Sema);
}
int __saveds __asm EndSystemJob(register __d0 int rc, register __d1 struct SystemECdata * data)
{
if (!data->flags)
Log(MSG_JOB_ENDED, data->jobNo);
FreeJobNum(data->jobNo);
ObtainSemaphore(&jobSema->mps_Sema);
NumSystemJobs--;
jobQueue[data->queueNo].jq_Cur--;
ReleaseSemaphore(&jobSema->mps_Sema);
FreeVec(data);
return rc;
}
void StartRexxJob(struct CyberNode * cn)
{
struct RexxMsg *msg;
struct MsgPort *rexxServerPort;
UWORD jobNo;
BPTR ifh, ofh;
if (cn->cn_Command[0] == '`') {
if (cn->cn_Args)
sprintf(SJ_Buf, "%s %s", &cn->cn_Command[1], cn->cn_Args);
else
strcpy(SJ_Buf, &cn->cn_Command[1]);
sprintf(CN_Buf, "\"%s\"", SJ_Buf);
}
else {
strcpy(SJ_Buf, cn->cn_Command);
sprintf(CN_Buf, "\"»%s«%s%s\"", cn->cn_Command, (cn->cn_Args ? " " : NULL), cn->cn_Args);
}
if (GetARexxLib() == NULL) {
ErrorMsg(MSG_CANT_START_AREXX_NO_LIB, CN_Buf, RXSNAME);
Log(MSG_CANT_START_AREXX_NO_LIB, CN_Buf, RXSNAME);
return;
}
jobNo = GetJobNum();
if (!jobNo) {
Log(MSG_JOB_TABLE_FULL, CN_Buf);
FreeARexxLib();
return;
}
if (cn->cn_ReDirIn)
ifh = Open(cn->cn_ReDirIn, MODE_OLDFILE);
else
ifh = Open("NIL:", MODE_OLDFILE);
if (!ifh) {
Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIRIN), CN_Buf);
FreeJobNum(jobNo);
FreeARexxLib();
return;
}
if (cn->cn_SendToUser && SendMailCmd[0])
ofh = SetupSendMail((cn->cn_Command[0] == '`' ? SJ_Buf : cn->cn_Command),
(cn->cn_Command[0] == '`' ? NULL : cn->cn_Args), cn->cn_SendToUser);
else {
if (cn->cn_ReDirOut)
ofh = Open(cn->cn_ReDirOut, (cn->cn_Flags & CNF_OUTAPP ? MODE_READWRITE : MODE_NEWFILE));
else
ofh = Open("NIL:", MODE_NEWFILE);
if (ofh && (cn->cn_Flags & CNF_OUTAPP))
Seek(ofh, 0, OFFSET_END);
}
if (!ofh) {
Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(&LocaleInfo, MSG_REDIROUT), CN_Buf);
Close(ifh);
FreeJobNum(jobNo);
FreeARexxLib();
return;
}
if (!(msg = CreateRexxMsg(RexxPort, "rexx", RXSDIR))) {
Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(&LocaleInfo, MSG_REXXMSG), CN_Buf);
Close(ofh);
Close(ifh);
FreeJobNum(jobNo);
FreeARexxLib();
return;
}
if (!(msg->rm_Args[0] = CreateArgstring(SJ_Buf, strlen(SJ_Buf)))) {
Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(&LocaleInfo, MSG_ARGSTRING), CN_Buf);
DeleteRexxMsg(msg);
Close(ofh);
Close(ifh);
FreeJobNum(jobNo);
FreeARexxLib();
return;
}
if (cn->cn_Args && cn->cn_Command[0] != '`') {
if (!(msg->rm_Args[1] = CreateArgstring(cn->cn_Args, strlen(cn->cn_Args)))) {
Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(&LocaleInfo, MSG_ARGSTRING), CN_Buf);
DeleteArgstring(msg->rm_Args[0]);
DeleteRexxMsg(msg);
Close(ofh);
Close(ifh);
FreeJobNum(jobNo);
FreeARexxLib();
return;
}
}
else
msg->rm_Args[1] = NULL;
msg->rm_Action = RXFUNC;
if (cn->cn_Command[0] == '`')
msg->rm_Action |= RXFF_STRING;
if (msg->rm_Args[1])
msg->rm_Action |= 1; /* one argument string passed in */
msg->rm_Args[2] = (STRPTR) jobNo;
msg->rm_Args[3] = (STRPTR) (cn->cn_Flags & CNF_NOLOG);
msg->rm_Args[4] = (STRPTR) cn->cn_ObeyQueue;
msg->rm_Stdin = ifh;
msg->rm_Stdout = ofh;
Forbid();
if (rexxServerPort = FindPort(RXSDIR))
PutMsg(rexxServerPort, (struct Message *) msg);
Permit();
if (rexxServerPort) {
if (!msg->rm_Args[3])
Log(MSG_JOB_STARTED, jobNo, CN_Buf);
ObtainSemaphore(&jobSema->mps_Sema);
NumARexxJobs++;
jobQueue[cn->cn_ObeyQueue].jq_Cur++;
ReleaseSemaphore(&jobSema->mps_Sema);
}
else {
Log(MSG_CANT_FIND_REXX_HOST, RXSDIR, CN_Buf);
if (msg->rm_Args[1])
DeleteArgstring(msg->rm_Args[1]);
DeleteArgstring(msg->rm_Args[0]);
DeleteRexxMsg(msg);
Close(ofh);
Close(ifh);
FreeJobNum(jobNo);
}
FreeARexxLib();
}
/*
* this routine will attempt to get a job number for us. it returns the job
* number or 0 if no jobs are free.
*/
UWORD GetJobNum(void)
{
register UWORD job;
register int index;
register UBYTE mask;
ObtainSemaphore(&jobSema->mps_Sema);
for (job = 0; job < JOB_TABLE_SIZE * sizeof(UBYTE); job++) {
index = job / sizeof(UBYTE);
mask = 1L << (job - index * sizeof(UBYTE));
if (jobSema->mps_SemaData[index] & mask)
continue;
jobSema->mps_SemaData[index] |= mask;
ReleaseSemaphore(&jobSema->mps_Sema);
return (UWORD) (job + 1);
}
ReleaseSemaphore(&jobSema->mps_Sema);
return (UWORD) 0;
}
/* this routine will free a job number previously allocated */
void FreeJobNum(UWORD job)
{
register int index;
register UBYTE mask;
if (!job || job >= JOB_TABLE_SIZE * sizeof(UBYTE))
return;
job--;
index = job / sizeof(UBYTE);
mask = 1L << (job - index * sizeof(UBYTE));
ObtainSemaphore(&jobSema->mps_Sema);
jobSema->mps_SemaData[index] &= ~mask;
ReleaseSemaphore(&jobSema->mps_Sema);
}
void __stdargs Log(ULONG fmtId,...)
{
va_list args;
BPTR loghandle;
struct DateTime dat;
UBYTE Date[LEN_DATSTRING + 1];
UBYTE Time[LEN_DATSTRING + 1];
DateStamp(&dat.dat_Stamp);
dat.dat_Format = FORMAT_DOS;
dat.dat_Flags = 0;
dat.dat_StrDay = NULL;
dat.dat_StrDate = Date;
dat.dat_StrTime = Time;
memset(Date, 0, LEN_DATSTRING + 1);
memset(Time, 0, LEN_DATSTRING + 1);
DateToStr(&dat);
va_start(args, fmtId);
ObtainSemaphore(&logSema->mps_Sema);
if (LogFile[0]) {
if (OwnDevUnitBase)
if (LockDevUnit("LOG-UPDATE.LOCK", 0, CYBERCRON, 0L))
goto dieLog;
if (loghandle = Open(LogFile, MODE_READWRITE)) {
Seek(loghandle, 0, OFFSET_END);
FPrintf(loghandle, "(%s %s) ", Date, Time);
VFPrintf(loghandle, GetString(&LocaleInfo, fmtId), (LONG *) args);
FPutC(loghandle, '\n');
Close(loghandle);
}
if (OwnDevUnitBase)
FreeDevUnit("LOG-UPDATE.LOCK", 0);
}
dieLog:
ReleaseSemaphore(&logSema->mps_Sema);
va_end(args);
}
/*
* this routine sets up a public semaphore. If it already exists we just
* bump its use count up by one. Otherwise we have to allocate a new one.
* semaDataSize is a number of bytes to allocate with the semaphore that can
* be shared by those locking the semaphore. Let's us have "global" data
* between seperate processes. These routines don't care what is put in
* this data space. It will be cleared to 0s when first allocated, though.
*/
struct MyPublicSema *InitMyPublicSemaphore(STRPTR semaName, ULONG semaDataSize)
{
struct MyPublicSema *theSema;
ULONG semaNameLen;
if (!semaName)
return (struct MyPublicSema *) NULL;
/*
* we want the semaphore's name to be a longword in length, at least
* in space allocated for it
*/
semaNameLen = ((strlen(semaName) + 3) / 4) * 4;
Forbid();
if (theSema = (struct MyPublicSema *) FindSemaphore(semaName))
theSema->mps_UseCount++; /* only safe way to diddle
* with this is under
* Forbid()... sigh */
else {
/*
* we didn't find the semaphore around already so we have to
* allocate and initialize it
*/
if (!(theSema = AllocVec(sizeof(struct MyPublicSema) + semaNameLen + semaDataSize, MEMF_CLEAR | MEMF_PUBLIC))) {
Permit();
return (struct MyPublicSema *) NULL;
}
strcpy(theSema->mps_SemaName, semaName);
theSema->mps_Sema.ss_Link.ln_Name = theSema->mps_SemaName;
if (semaDataSize)
theSema->mps_SemaData = &theSema->mps_SemaName[semaNameLen];
theSema->mps_UseCount = 1;
AddSemaphore(&theSema->mps_Sema);
}
Permit();
return theSema;
}
/*
* this dealocates what we got from InitMyPublicSemaphore(). If there are
* others still using it then just down the use count. Otherwise remove the
* semaphore from the public lists and down the use count.
*/
void FreeMyPublicSemaphore(struct MyPublicSema * mySema)
{
if (mySema) {
ObtainSemaphore(&mySema->mps_Sema);
Forbid();
ReleaseSemaphore(&mySema->mps_Sema);
if (!--mySema->mps_UseCount) {
RemSemaphore(&mySema->mps_Sema);
FreeVec(mySema);
}
Permit();
}
}
/*
* this function will open the ARexx library for us. it handles nested
* calls to open the library such that we only call OpenLibrary() once. each
* time a rexx command is run, we call this routine to open the library and
* when the RexxMsg comes back we call FreeARexxLib() to decrement the nest
* count.
*/
BOOL GetARexxLib(void)
{
if (ARexxLibCount) {
ARexxLibCount++;
return TRUE;
}
if (!(RexxSysBase = (struct RxsLib *) OpenLibrary(RXSNAME, 0)))
return FALSE;
ARexxLibCount = 1;
return TRUE;
}
/*
* this routine is the opposite of GetARexxLib(). it frees a nested open
* count for the ARexx library. if the count goes to zero then we call
* CloseLibrary() to free the library for real.
*/
void FreeARexxLib(void)
{
if (!--ARexxLibCount)
CloseLibrary((struct Library *) RexxSysBase);
}
/*
* we use this function to send error messages to StdErr. We prefix all
* messages with "CyberCron:" since the message might be dumped into a
* stream with other people sending output to it
*/
void __stdargs ErrorMsg(ULONG fmtId,...)
{
va_list args;
struct IntuitionBase *IntuitionBase;
ULONG my_IDCMP;
struct EasyStruct ezRequest;
if (ErrorsToStdErr) {
if (StdErr) {
FPrintf(StdErr, "%s: ", CYBERCRON);
va_start(args, fmtId);
VFPrintf(StdErr, GetString(&LocaleInfo, fmtId), (LONG *) args);
FPutC(StdErr, '\n');
Flush(StdErr);
va_end(args);
}
}
else {
/* grab intuition, do an EasyRequest and then free intuition */
if (IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37L)) {
ezRequest.es_StructSize = sizeof(struct EasyStruct);
ezRequest.es_Flags = 0;
ezRequest.es_Title = GetString(&LocaleInfo, MSG_REQWINTITLE);
ezRequest.es_TextFormat = GetString(&LocaleInfo, fmtId);
ezRequest.es_GadgetFormat = GetString(&LocaleInfo, MSG_CONTINUE_GAD);
my_IDCMP = 0L;
va_start(args, fmtId);
EasyRequestArgs((struct Window *) NULL, &ezRequest, &my_IDCMP, args);
va_end(args);
CloseLibrary((struct Library *) IntuitionBase);
}
}
}
void GetSystemTime(SystemTime_t * st, BOOL stIsSet)
{
struct timeval tr_time;
struct ClockData cd;
if (!stIsSet) {
GetSysTime(&tr_time);
st->st_tvsecs = tr_time.tv_secs;
}
Amiga2Date(st->st_tvsecs, &cd);
st->st_Year = cd.year;
st->st_Month = cd.month;
st->st_Day = cd.mday;
st->st_Hour = cd.hour;
st->st_Min = cd.min;
st->st_Sec = cd.sec;
st->st_DOW = cd.wday;
}